﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace AhpilyServer
{
    /// <summary>
    /// 服务器端     
    /// </summary>
    public class ServerPeer
    {
        /// <summary>
        /// 服务器端的socket对象
        /// </summary>
        private Socket serverSocket;
        /// <summary>
        /// 现在可同时访问同一资源 或 资源池的 线程数
        /// </summary>
        private Semaphore acceptSemaphore;

        /// <summary>
        /// 客户端对象的连接池
        /// </summary>
        private ClientPeerPool ClientPeerPool;

        /// <summary>
        /// 应用层
        /// </summary>
        private IApplication application;

        /// <summary>
        /// 设置应用层
        /// </summary>
        /// <param name="app"></param>
        public void SetApplication(IApplication app)
        {
            this.application = app;
        }

        /// <summary>
        /// 开启服务器
        /// </summary>
        /// <param name="port">监听端口</param>
        /// <param name="maxCount">最大连接数量</param>
        public void Start(int port, int maxCount)
        {
            try
            {
                // 创建服务器Socket
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                // 初始化对象 初始入口数 最大并发数
                acceptSemaphore = new Semaphore(maxCount, maxCount);
                // 服务器绑定本地节点
                serverSocket.Bind(new IPEndPoint(IPAddress.Any, port));

                ClientPeerPool = new ClientPeerPool(maxCount);
                ClientPeer tmpClientPeer = null;
                for(int i = 0; i < maxCount; ++i)
                {
                    tmpClientPeer = new ClientPeer();  // 创建
                    tmpClientPeer.ReceiveArgs.Completed += Receive_Completed;
                    tmpClientPeer.receiveCompleted += receiveCompleted;
                    tmpClientPeer.sendDisconnect = Disconnect;
                    ClientPeerPool.Enqueue(tmpClientPeer); // 入池
                }


                // 开始监听
                // 因为有专门的连接数量处理
                // 可以随便填数
                serverSocket.Listen(maxCount);
                // 界面提示
                Console.WriteLine("服务器启动成功。。。");

                StartAccept(null);
            } catch(Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        #region 接受客户端的连接
        /// <summary>
        /// 开启接受客户端的连接
        /// </summary>
        private void StartAccept(SocketAsyncEventArgs e)
        {
            // 前置处理
            if(e == null)
            {
                e = new SocketAsyncEventArgs();
                // 绑定一个回调事件
                e.Completed += Accept_Completed;
            }



            // 开启一个异步操作来接受一个传入的连接操作
            bool result = serverSocket.AcceptAsync(e);
            // 判断异步操作是否执行完毕
            // true 代表正在执行中
            // false 执行完毕
            if(result == false)
            {
                /// 处理连接请求
                ProcessAccept(e);
            }
        }

        /// <summary>
        /// 处理连接请求
        /// </summary>
        /// <param name="e"></param>
        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            // 计数：客户端连接数
            // 阻止当前线程， 直到当前 WaitHandle 收到信息
            acceptSemaphore.WaitOne();

            // 得到客户端的对象
            Socket clientSocket = e.AcceptSocket;
            // 在进行保存处理
            ClientPeer client = ClientPeerPool.Dequeue();
            client.ClientSocket = e.AcceptSocket;

            Console.WriteLine("客户端连接成功：" + client.ClientSocket.RemoteEndPoint.ToString());

            // 开始接受数据
            StartReceive(client);

            // 递归调用
            e.AcceptSocket = null;
            StartAccept(e);
        }

        /// <summary>
        /// 接受连接请求异步事件完成时候触发
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Accept_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept(e);
        }

        #endregion



        #region 接收数据

        private void StartReceive(ClientPeer client)
        {
            try
            {
                bool result = client.ClientSocket.ReceiveAsync(client.ReceiveArgs);
                if(result == false)
                {
                    ProcessReceive(client.ReceiveArgs);
                }
            } catch(Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        /// <summary>
        /// 处理接受的请求
        /// </summary>
        /// <param name="e"></param>
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            ClientPeer client = e.UserToken as ClientPeer;

            // 判断网络消息是否接收成功
            if(client.ReceiveArgs.SocketError == SocketError.Success && client.ReceiveArgs.BytesTransferred > 0)
            {
                // 将接收成的消息放到一个字节数组
                byte[] packet = new byte[client.ReceiveArgs.BytesTransferred];
                Buffer.BlockCopy(client.ReceiveArgs.Buffer, 0, packet, 0, client.ReceiveArgs.BytesTransferred);

                // 让客户端对象自身处理这个数据包 自身解析
                client.StartReceive(packet);

                // 尾递归
                StartReceive(client);
            } else if(client.ReceiveArgs.BytesTransferred == 0)
            {
                // 断开了连接 如果没有传输的字节数 就代表断开连接了
                {
                    if(client.ReceiveArgs.SocketError == SocketError.Success)
                    {
                        // 客户端主动断开连接
                        Disconnect(client, "客户端主动断开连接");
                    } else
                    {
                        // 由于网络异常被动断开连接
                        Disconnect(client, client.ReceiveArgs.SocketError.ToString());
                    }
                }
            }
        }

        /// <summary>
        /// 当接收完成时 触发的事件
        /// </summary>
        /// <param name="e"></param>
        private void Receive_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessReceive(e);
        }

        /// <summary>
        /// 一条数据解析完成的处理
        /// </summary>
        /// <param name="client">对应的连接对象</param>
        /// <param name="vadlue">EncodeTool解析出来的一个具体能使用的类型</param>
        private void receiveCompleted(ClientPeer client, SocketMsg msg)
        {
            // 给应用层，让其使用
            application.OnReceive(client, msg);
        }
        #endregion

        #region 断开连接
        /// <summary>
        /// 断开连接
        /// </summary>
        /// <param name="client">断开的客户端连接对象</param>
        /// <param name="reason">断开的原因</param>
        public void Disconnect(ClientPeer client, string reason)
        {
            try
            {
                // 清空一些数据
                if(client == null)
                {
                    throw new Exception("当前指定的客户端连接对象为空");
                }

                Console.WriteLine(client.ClientSocket.RemoteEndPoint.ToString() + "客户端断开连接 原因：" + reason);

                // 通知应用层 这个客户端断开连接了
                application.OnDisconnect(client);

                client.Disconnect();
                ClientPeerPool.Enqueue(client);
                acceptSemaphore.Release();
            } catch(Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
        #endregion
    }
}
